package org.codefilarete.stalactite.sql;

import org.codefilarete.stalactite.query.builder.QuerySQLBuilderFactory;
import org.codefilarete.stalactite.sql.ServiceLoaderDialectResolver.DatabaseSignet;
import org.codefilarete.stalactite.sql.ddl.DDLGenerator;
import org.codefilarete.stalactite.sql.ddl.DDLSequenceGenerator;
import org.codefilarete.stalactite.sql.ddl.DDLTableGenerator;
import org.codefilarete.stalactite.sql.ddl.SqlTypeRegistry;
import org.codefilarete.stalactite.sql.statement.DMLGenerator;
import org.codefilarete.stalactite.sql.statement.GeneratedKeysReader;
import org.codefilarete.stalactite.sql.statement.ReadOperationFactory;
import org.codefilarete.stalactite.sql.statement.WriteOperationFactory;
import org.codefilarete.stalactite.sql.statement.binder.ColumnBinderRegistry;

/**
 * Class that keeps objects necessary to "communicate" with a Database at the SQL language level:
 * - column types for their creation: {@link SqlTypeRegistry} 
 * - column types for their read and write in {@link java.sql.PreparedStatement} and {@link java.sql.ResultSet}: {@link ColumnBinderRegistry}
 * - engines for SQL generation: {@link DDLGenerator} and {@link DMLGenerator}
 * 
 * @author Guillaume Mary
 */
public interface Dialect {
	
	DatabaseSignet getCompatibility();
	
	DDLTableGenerator getDdlTableGenerator();
	
	DDLSequenceGenerator getDdlSequenceGenerator();
	
	DMLGenerator getDmlGenerator();
	
	WriteOperationFactory getWriteOperationFactory();
	
	ReadOperationFactory getReadOperationFactory();
	
	QuerySQLBuilderFactory getQuerySQLBuilderFactory();
	
	SqlTypeRegistry getSqlTypeRegistry();
	
	ColumnBinderRegistry getColumnBinderRegistry();
	
	DMLNameProviderFactory getDmlNameProviderFactory();
	
	int getInOperatorMaxSize();
	
	default <I> GeneratedKeysReader<I> buildGeneratedKeysReader(String keyName, Class<I> columnType) {
		return getGeneratedKeysReaderFactory().build(keyName, columnType);
	}
	
	GeneratedKeysReaderFactory getGeneratedKeysReaderFactory();
	
	DatabaseSequenceSelectorFactory getDatabaseSequenceSelectorFactory();
	
	/**
	 * Indicates if this dialect supports what ANSI-SQL terms "row value constructor" syntax, also called tuple syntax.
	 * Basically, does it support syntax like <pre>"... where (FIRST_NAME, LAST_NAME) = ('John', 'Doe')"</pre>.
	 *
	 * @return true if this SQL dialect supports "row value constructor" syntax, false otherwise.
	 */
	boolean supportsTupleCondition();
	
	/**
	 * {@link Dialect} implementation based on fields to store instances required by {@link Dialect} methods. 
	 * 
	 * @author Guillaume Mary
	 */
	class DialectSupport implements Dialect {
		
		private final DatabaseSignet compatibility;
		
		private final DDLTableGenerator ddlTableGenerator;
		
		private final DDLSequenceGenerator ddlSequenceGenerator;
		
		private final DMLGenerator dmlGenerator;
		
		private final WriteOperationFactory writeOperationFactory;
		
		private final ReadOperationFactory readOperationFactory;
		
		private final QuerySQLBuilderFactory querySQLBuilderFactory;
		
		private final SqlTypeRegistry sqlTypeRegistry;
		
		private final ColumnBinderRegistry columnBinderRegistry;
		
		private final DMLNameProviderFactory dmlNameProviderFactory;
		
		private final int inOperatorMaxSize;
		
		private final GeneratedKeysReaderFactory generatedKeysReaderFactory;
		
		private final boolean supportsTupleCondition;
		
		private final DatabaseSequenceSelectorFactory databaseSequenceSelectorFactory;
		
		public DialectSupport(DatabaseSignet compatibility,
							  DDLTableGenerator ddlTableGenerator,
							  DDLSequenceGenerator ddlSequenceGenerator,
							  DMLGenerator dmlGenerator,
							  WriteOperationFactory writeOperationFactory,
							  ReadOperationFactory readOperationFactory,
							  QuerySQLBuilderFactory querySQLBuilderFactory,
							  SqlTypeRegistry sqlTypeRegistry,
							  ColumnBinderRegistry columnBinderRegistry,
							  DMLNameProviderFactory dmlNameProviderFactory,
							  int inOperatorMaxSize,
							  GeneratedKeysReaderFactory generatedKeysReaderFactory,
							  DatabaseSequenceSelectorFactory databaseSequenceSelectorFactory,
							  boolean supportsTupleCondition) {
			this.compatibility = compatibility;
			this.ddlTableGenerator = ddlTableGenerator;
			this.ddlSequenceGenerator = ddlSequenceGenerator;
			this.dmlGenerator = dmlGenerator;
			this.writeOperationFactory = writeOperationFactory;
			this.readOperationFactory = readOperationFactory;
			this.querySQLBuilderFactory = querySQLBuilderFactory;
			this.sqlTypeRegistry = sqlTypeRegistry;
			this.columnBinderRegistry = columnBinderRegistry;
			this.dmlNameProviderFactory = dmlNameProviderFactory;
			this.inOperatorMaxSize = inOperatorMaxSize;
			this.generatedKeysReaderFactory = generatedKeysReaderFactory;
			this.databaseSequenceSelectorFactory = databaseSequenceSelectorFactory;
			this.supportsTupleCondition = supportsTupleCondition;
		}
		
		@Override
		public DatabaseSignet getCompatibility() {
			return compatibility;
		}
		
		@Override
		public DDLTableGenerator getDdlTableGenerator() {
			return ddlTableGenerator;
		}
		
		@Override
		public DDLSequenceGenerator getDdlSequenceGenerator() {
			return ddlSequenceGenerator;
		}
		
		@Override
		public DMLGenerator getDmlGenerator() {
			return dmlGenerator;
		}
		
		@Override
		public WriteOperationFactory getWriteOperationFactory() {
			return writeOperationFactory;
		}
		
		@Override
		public ReadOperationFactory getReadOperationFactory() {
			return readOperationFactory;
		}
		
		@Override
		public QuerySQLBuilderFactory getQuerySQLBuilderFactory() {
			return querySQLBuilderFactory;
		}
		
		@Override
		public SqlTypeRegistry getSqlTypeRegistry() {
			return sqlTypeRegistry;
		}
		
		@Override
		public ColumnBinderRegistry getColumnBinderRegistry() {
			return columnBinderRegistry;
		}
		
		@Override
		public DMLNameProviderFactory getDmlNameProviderFactory() {
			return dmlNameProviderFactory;
		}
		
		@Override
		public int getInOperatorMaxSize() {
			return inOperatorMaxSize;
		}
		
		@Override
		public GeneratedKeysReaderFactory getGeneratedKeysReaderFactory() {
			return generatedKeysReaderFactory;
		}
		
		@Override
		public boolean supportsTupleCondition() {
			return supportsTupleCondition;
		}
		
		@Override
		public DatabaseSequenceSelectorFactory getDatabaseSequenceSelectorFactory() {
			return databaseSequenceSelectorFactory;
		}
	}
}
